window.Mrtc = (function () {
    var MrtcSessionMap = new Map();
    var callback = {}
    var unpublishCallback = {}

    function StreamParam(roomName, displayName, randomNum, params) {
        if (roomName == undefined || displayName == undefined || randomNum == undefined) return undefined;
        return {
            room: roomName,
            name: displayName,
            random: randomNum,
            streamKey: roomName + "." + displayName + "." + randomNum,
            params: params
        }
    }

    var IsAppleDevice = function () {
        if ((navigator.userAgent.match(/iPhone/i)) ||
            (navigator.userAgent.match(/iPod/i)) ||
            (navigator.userAgent.match(/iPad/i))) {
            return true;
        } else {
            return false;
        }
    }

    function MrtcSessionClient(host) {
        /* 一个mrtc服务地址，对应一个Client对象，
         * 同一个服务地址不同房间，推拉流都公用一个 client
         */
        var server = ''
        if (host.includes(':')) {
            if ("https:" == document.location.protocol) {
                server = "wss://" + host
            } else {
                server = "ws://" + host
            }
        } else {
            if ("https:" == document.location.protocol) {
                server = "wss://" + host + ':7443'
            } else {
                server = "ws://" + host + ':7080'
            }
        }
        server += "/mrtc"

        // var server = ("https:" == document.location.protocol ? ("wss://" + host) : ("ws://" + host));
        var mrtc = null;
        var pvtid = null;
        // key:room.name, value:videopar 保存获取到的 正在播放的流信息，包括不同房间
        var streamMap = new Map();
        var playerMap = new Map(); // key:room.name. value:video dom
        var playerVolumeMap = new Map(); // save div tag volume
        var playerAttachSet = new Set(); //多播控制，已经进行连接观看, 是否需要删除？
        var publishMap = new Map(); // key:room.name. value:video dom
        // key:room.name value:push handle 播放者操作句柄
        var playHandleMap = new Map();
        // key:room.name value:push handle 推流者查找句柄
        var publishHandleMap = new Map();
        var initsuccess = false; // MrtcSession 初始化成功
        // 由于可能在初始化未完成的情况下，调用播放和推流接口，保存需要推流、播放的列表。
        var playerList = [];
        var publishList = [];
        var localStream; // 保存本地流

        var reconnectObj = null;
        var userAgent = navigator.userAgent;
        var isSafari = userAgent.indexOf("Safari") > -1 && userAgent.indexOf("Chrome") == -1; //判断是否Safari浏览器

        if (IsAppleDevice() && !window.hasSetWXAutoplay) {
            window.hasSetWXAutoplay = true;
            HTMLVideoElement.prototype._play = HTMLVideoElement.prototype.play;
            var wxPlayVideo = function (video) {
                WeixinJSBridge.invoke('getNetworkType', {}, function (e) {
                    video._play();
                });
            }

            var play = function () {
                var self = this;
                self._play();
                var evtFns = [];
                try {
                    wxPlayVideo(self);
                    return;
                } catch (ex) {
                    evtFns.push("WeixinJSBridgeReady", function evt() {
                        wxPlayVideo(self);
                        for (var i = 0; i < evtFns.length; i += 2) document.removeEventListener(evtFns[i], evtFns[i + 1], false);
                    });
                    document.addEventListener("WeixinJSBridgeReady", evtFns[evtFns.length - 1], false);
                }
            }
            HTMLVideoElement.prototype.play = play;
        }

        function APHash(str) {

            var temp = 0;
            if (/^\d{1,16}$/.test(str)) {
                temp = parseInt(str);
            } else {
                str += '';
                var hash = 0,
                    i = 0;
                for (i = 0; i < str.length; i++) {
                    if ((i & 1) == 0) {
                        hash ^= ((hash << 7) ^ str.charCodeAt(i) ^ (hash >> 3));
                    } else {
                        hash ^= (~((hash << 11) ^ str.charCodeAt(i) ^ (hash >> 5)));
                    }
                }
                temp = (hash & 0x7FFFFFFF);
            }
            return temp
        }

        init();

        function reconnect() {
            if (initsuccess) {
                reconnectObj = null;
            } else {
                reconnectObj = setTimeout(reconnect, 5000);
                MrtcSession.log("Try to reconnect websocket");
                init();
            }
        }

        function init() {
            MrtcSession.init({
                debug: "all",
                callback: function () {
                    mrtc = new MrtcSession({
                        server: server,
                        success: function () {
                            // 将推流和播流列表创建创建房间
                            initsuccess = true;
                            for (i in playerList) {
                                attachRoom(playerList[i], false);
                            }
                            for (i in publishList) {
                                attachRoom(publishList[i], true);
                            }
                        },
                        error: function (error) {
                            MrtcSession.error(error);
                            // init();
                        },
                        destroyed: function () {
                            window.location.reload();
                        },
                        disconnected: function () {
                            MrtcSession.error("Error websocket disconnected");
                            initsuccess = false;
                            playerAttachSet = new Set();
                            reconnect();
                        }
                    });
                }
            });
        }

        function publishOwnFeed(streamParam, useAudio) {
            // Publish our stream
            var handler = publishHandleMap.get(streamParam.streamKey);
            handler.createOffer({
                // Add data:true here if you want to publish datachannels as well
                media: {
                    video: streamParam.params.type,
                    location: streamParam.params.location, // screenandcamera camera location
                    scale: streamParam.params.scale, // screenandcamera camera scale
                    videoDeviceId: streamParam.params.videoDeviceId,
                    audioDeviceId: streamParam.params.audioDeviceId,
                    openCamera: streamParam.params.openCamera,
                    codec: streamParam.params.codec,
                    audioRecv: false,
                    videoRecv: false,
                    audioSend: useAudio,
                    videoSend: true,
                    resolution: streamParam.params.resolution
                }, // Publishers are sendonly
                success: function (jsep) {
                    MrtcSession.debug("Got publisher SDP!");
                    MrtcSession.debug(jsep);
                    var publish = {
                        "request": "configure",
                        "audio": useAudio,
                        "video": true
                    };
                    handler.send({
                        "message": publish,
                        "jsep": jsep
                    });
                    dispatch(streamParam, 'on', streamParam)

                },
                error: function (error) {
                    MrtcSession.error("WebRTC error:", error);
                    dispatch(streamParam, 'error', error)
                    // if (useAudio) {
                    //     publishOwnFeed(streamParam, false);
                    // } else {
                    //     publishOwnFeed(streamParam, true);
                    // }
                }
            });
        }

        function dispatch(streamParam, name, ...args) {
            if (streamParam.params.callback && streamParam.params.callback[name] && streamParam.params.callback[name].length) {
                streamParam.params.callback[name].forEach((fn) => {
                    fn(args)
                })
            }
        }

        function attachRoom(streamParam, isPublish) {

            var roomid = APHash(streamParam.room);
            mrtc.attach({
                plugin: "mrtc.plugin.videoroom",
                success: function (pluginHandle) {
                    handler = pluginHandle;
                    if (isPublish) {
                        publishHandleMap.set(streamParam.streamKey, handler);
                    } else {
                        playHandleMap.set(streamParam.streamKey, handler);
                    }
                    MrtcSession.log("Plugin attached! (" + handler.getPlugin() + ", id=" + handler.getId() + ")");
                    MrtcSession.log("  -- This is a publisher/manager");
                    // 如果是观看者，需要拉取视频列表，需要以推流者加入房间。
                    var display = "MuduWatchDoge";
                    if (isPublish) {
                        display = streamParam.name;
                    }
                    var bitrate = (streamParam && streamParam.params && streamParam.params.bitrate) ? streamParam.params.bitrate : 1500000
                    var resolution = (streamParam && streamParam.params && streamParam.params.resolution) ? streamParam.params.resolution : 1280 * 720
                    var register = {
                        "request": "joinandconfigure",
                        "room": roomid,
                        "ptype": "publisher",
                        "display": display,
                        "bitrate": bitrate,
                        "resolution": resolution
                    };
                    handler.send({
                        "message": register
                    });

                    if (streamParam.successCallback) {
                        streamParam.successCallback();
                        delete streamParam.successCallback
                    }
                },
                error: function (error) {
                    MrtcSession.error("  -- Error attaching plugin...", error);
                    dispatch(streamParam, 'error', error)
                },
                consentDialog: function (on) {
                    MrtcSession.debug("Consent dialog should be " + (on ? "on" : "off") + " now");
                },
                mediaState: function (medium, on) {
                    MrtcSession.log("MrtcSession " + (on ? "started" : "stopped") + " receiving our " + medium);
                },
                webrtcState: function (on) {
                    MrtcSession.log("MrtcSession says our WebRTC PeerConnection is " + (on ? "up" : "down") + " now");
                    if (on) {
                        dispatch(streamParam, 'on', streamParam)
                    } else {
                        dispatch(streamParam, 'out', streamParam)
                    }
                },
                onmessage: function (msg, jsep) {
                    dealWithMessage(streamParam, msg, jsep, isPublish);
                },
                onlocalstream: function (stream) {
                    // 加载本地视频
                    if (isPublish) {
                        localStream = stream;
                        MrtcSession.debug(stream);
                        var domid = publishMap.get(streamParam.streamKey);
                        while (domid.firstChild) {
                            domid.removeChild(domid.firstChild);
                        }
                        var video = document.createElement('video');
                        video.setAttribute('id', 'localVideo')
                        video.setAttribute('width', '100%')
                        video.setAttribute('height', '100%')
                        video.setAttribute('autoplay', '')
                        if (IsAppleDevice()) {
                            video.setAttribute('controls', '')
                        }
                        video.setAttribute('playsinline', '')
                        video.muted = true;
                        domid.appendChild(video);
                        MrtcSession.attachMediaStream(video, stream);
                        if (isSafari) {
                            video.play();
                        }
                    }
                },
                onremotestream: function (stream) {
                },
                oncleanup: function () {
                    // 移除本地视频
                    localStream = null;
                    var domid = publishMap.get(streamParam.streamKey);
                    while (domid.firstChild) {
                        domid.removeChild(domid.firstChild);
                    }
                    if (unpublishCallback && unpublishCallback[streamParam.streamKey]&& unpublishCallback[streamParam.streamKey].unpublishCallback) {
                        unpublishCallback[streamParam.streamKey].unpublishCallback();
                        delete unpublishCallback[streamParam.streamKey].unpublishCallback
                    }
                }
            });
        }

        function dealWithMessage(streamParam, msg, jsep, isPublish = false) {
            MrtcSession.debug(" ::: Got a message (publisher) :::");
            MrtcSession.debug(msg);
            var event = msg["videoroom"];
            MrtcSession.debug("Event: " + event);
            var roomid = APHash(streamParam.room);
            if (event != undefined && event != null) {
                if (event === "created") {
                    var handler;
                    if (isPublish) {
                        handler = publishHandleMap.get(streamName);
                    } else {
                        handler = playHandleMap.get(streamName);
                    }
                    var display = "MuduWatchDoge";
                    if (isPublish) {
                        display = streamParam.name;
                    }
                    var register = {
                        "request": "join",
                        "room": roomid,
                        "ptype": "publisher",
                        "display": display
                    };
                    handler.send({
                        "message": register
                    });
                } else if (event === "joined") {
                    pvtid = msg["private_id"];
                    if (isPublish) publishOwnFeed(streamParam, true);
                    if (msg["publishers"] !== undefined && msg["publishers"] !== null) {
                        var list = msg["publishers"];
                        MrtcSession.debug("Got a list of available publishers/feeds:");
                        MrtcSession.debug(list);
                        // 加入房间时会返回当前说有视频
                        for (var f in list) {
                            var id = list[f]["id"];
                            var display = list[f]["display"];
                            var audio = list[f]["audio_codec"];
                            var video = list[f]["video_codec"];
                            MrtcSession.debug("  >> [" + id + "] " + display + " (audio: " + audio + ", video: " + video + ")");
                            var streamPar = {};
                            streamPar.id = id;
                            streamPar.roomid = roomid;
                            streamPar.display = display;
                            streamPar.audio = audio;
                            streamPar.video = video;
                            for (let item of playerMap) {
                                if (item[0].indexOf(streamParam.room + "." + display + "." + streamParam.random) != -1 &&
                                    !playerAttachSet.has(item[0])) {
                                    playerAttachSet.add(item[0]);
                                    streamMap.set(item[0], streamPar);
                                    newRemoteFeed(item[0]);
                                }
                            }
                        }
                    }
                } else if (event === "destroyed") {
                    // The room has been destroyed
                } else if (event === "event") {
                    // Any new feed to attach to?

                    if (msg["publishers"] !== undefined && msg["publishers"] !== null) {
                        var list = msg["publishers"];
                        MrtcSession.debug("Got a list of available publishers/feeds:");
                        MrtcSession.debug(list);
                        // 加入房间后，有流推到相同房间，会实时通知。
                        for (var f in list) {
                            var id = list[f]["id"];
                            var display = list[f]["display"];
                            var audio = list[f]["audio_codec"];
                            var video = list[f]["video_codec"];
                            MrtcSession.debug("  >> [" + id + "] " + display + " (audio: " + audio + ", video: " + video + ")");
                            var streamPar = {};
                            streamPar.id = id;
                            streamPar.roomid = roomid;
                            streamPar.display = display;
                            streamPar.audio = audio;
                            streamPar.video = video;
                            for (var item of playerMap) {
                                if (item[0].indexOf(streamParam.room + "." + display + "." + streamParam.random) != -1 &&
                                    !playerAttachSet.has(item[0])) {
                                    playerAttachSet.add(item[0]);
                                    streamMap.set(item[0], streamPar);
                                    newRemoteFeed(item[0]);
                                }
                            }
                        }
                    } else if (msg["leaving"] !== undefined && msg["leaving"] !== null) {
                        // One of the publishers has gone away?
                        var leaving = msg["leaving"];
                        MrtcSession.log("Publisher left: " + leaving);
                        var remoteFeed = null;
                        // 离开房间时，删除视频列表，删除流。
                        for (let item of streamMap) {
                            if (item[1].feed != null && item[1].feed != undefined && item[1].id == leaving) {
                                remoteFeed = item[1].feed;
                                streamMap.delete(item[0]);
                                playerAttachSet.delete(item[0]);
                                break;
                            }
                        }

                        if (remoteFeed != null) {
                            remoteFeed.detach();
                        }
                    } else if (msg["unpublished"] !== undefined && msg["unpublished"] !== null) {
                        // One of the publishers has unpublished?
                        var unpublished = msg["unpublished"];
                        MrtcSession.log("Publisher left: " + unpublished);
                        var remoteFeed = null;
                        for (let item of streamMap) {
                            if (item[1].feed != null && item[1].feed != undefined && item[1].id == unpublished) {
                                remoteFeed = item[1].feed;
                                streamMap.delete(item[0]);
                                playerAttachSet.delete(item[0]);
                                break;
                            }
                        }

                        if (remoteFeed != null) {
                            remoteFeed.detach();
                        }
                    } else if (msg["error"] !== undefined && msg["error"] !== null) {
                        if (msg["error_code"] === 426) {
                            // 如果当前房间未创建，创建房间。
                            var handler;
                            if (isPublish) {
                                handler = publishHandleMap.get(streamParam.streamKey);
                            } else {
                                handler = playHandleMap.get(streamParam.streamKey);
                            }
                            var register = {
                                "request": "create",
                                "room": roomid,
                                "videocodec": streamParam.params.codec || "h264",
                                "publishers": 64
                            };

                            handler.send({
                                "message": register,
                                success: function () {
                                    // 在创建房间后，再加入房间。
                                    if (isPublish) {
                                        display = streamParam.name;
                                    }
                                    var register = {
                                        "request": "join",
                                        "room": roomid,
                                        "ptype": "publisher",
                                        "display": display
                                    };
                                    handler.send({
                                        "message": register
                                    });
                                }
                            });
                            // This is a "no such room" error: give a more meaningful description
                        } else {
                        }
                    }
                }
            }
            if (jsep !== undefined && jsep !== null) {
                // sdp 交互
                MrtcSession.debug(jsep);
                var handler;
                if (isPublish) {
                    handler = publishHandleMap.get(streamParam.streamKey);
                } else {
                    handler = playHandleMap.get(streamParam.streamKey);
                }
                handler.handleRemoteJsep({
                    jsep: jsep
                });
                // Check if any of the media we wanted to publish has
                // been rejected (e.g., wrong or unsupported codec)
                var audio = msg["audio_codec"];
                if (localStream && localStream.getAudioTracks() && localStream.getAudioTracks().length > 0 && !audio) {
                    // Audio has been rejected
                    toastr.warning("Our audio stream has been rejected, viewers won't hear us");
                }
                var video = msg["video_codec"];
                if (localStream && localStream.getVideoTracks() && localStream.getVideoTracks().length > 0 && !video) {
                    // Video has been rejected
                    toastr.warning("Our video stream has been rejected, viewers won't hear us");
                }
            }
        }

        function newRemoteFeed(streamKey) {
            // A new feed has been published, create a new plugin handle and attach to it as a listener
            var remoteFeed = null;
            var streamPar = streamMap.get(streamKey);
            mrtc.attach({
                plugin: "mrtc.plugin.videoroom",
                success: function (pluginHandle) {
                    remoteFeed = pluginHandle;
                    MrtcSession.log("Plugin attached! (" + remoteFeed.getPlugin() + ", id=" + remoteFeed.getId() + ")");
                    MrtcSession.log("  -- This is a subscriber");
                    // We wait for the plugin to send us an offer
                    // "request":"create","room":1006283977,"publishers":64
                    var listen = {
                        "request": "join",
                        "room": streamPar.roomid,
                        "ptype": "listener",
                        "feed": streamPar.id,
                        "private_id": pvtid
                    };
                    if (streamPar.video === "vp8" && adapter.browserDetails.browser === "safari") {
                        toastr.warning("Publisher is using VP8, but Safari doesn't support it: disabling video");
                        listen["offer_video"] = false;
                    }
                    remoteFeed.send({
                        "message": listen
                    });
                },
                error: function (error) {
                    MrtcSession.error("  -- Error attaching plugin...", error);
                },
                onmessage: function (msg, jsep) {
                    MrtcSession.debug(" ::: Got a message (listener) :::");
                    MrtcSession.debug(msg);
                    var event = msg["videoroom"];
                    MrtcSession.debug("Event: " + event);
                    if (msg["error"] !== undefined && msg["error"] !== null) {
                    } else if (event != undefined && event != null) {
                        if (event === "attached") {
                            // 将观看者炒作句柄保存到视频参数对象中
                            var display = msg["display"];
                            var streamPar = streamMap.get(streamKey);
                            streamPar.feed = remoteFeed;
                            streamMap.set(streamKey, streamPar);
                            MrtcSession.log("Successfully attached to feed (" + display + ") in room " + msg["room"]);
                        } else if (event === "event") {
                            // Check if we got an event on a simulcast-related event from this publisher
                        } else {
                            // What has just happened?
                        }
                    }
                    if (jsep !== undefined && jsep !== null) {
                        MrtcSession.debug("Handling SDP as well...");
                        MrtcSession.debug(jsep);
                        // Answer and attach
                        remoteFeed.createAnswer({
                            jsep: jsep,
                            // Add data:true here if you want to subscribe to datachannels as well
                            // (obviously only works if the publisher offered them in the first place)
                            media: {
                                audioSend: false,
                                videoSend: false
                            }, // We want recvonly audio/video
                            success: function (jsep) {
                                MrtcSession.debug("Got SDP!");
                                MrtcSession.debug(jsep);
                                var body = {
                                    "request": "start",
                                    "room": streamPar.roomid
                                };
                                remoteFeed.send({
                                    "message": body,
                                    "jsep": jsep
                                });
                            },
                            error: function (error) {
                                MrtcSession.error("WebRTC error:", error);
                            }
                        });
                    }
                },
                webrtcState: function (on) {
                    MrtcSession.log("MrtcSession says this WebRTC PeerConnection is " + (on ? "up" : "down") + " now");
                },
                onlocalstream: function (stream) {
                    // The subscriber stream is recvonly, we don't expect anything here
                },
                onremotestream: function (stream) {
                    // 得到远程视频
                    var domid = playerMap.get(streamKey);
                    var video = document.createElement('video');
                    video.setAttribute('id', 'localVideo')
                    video.setAttribute('width', '100%')
                    video.setAttribute('height', '100%')
                    video.setAttribute('autoplay', '')
                    video.setAttribute('playsinline', '')
                    var volumeValue = playerVolumeMap.get(streamKey);
                    if (volumeValue !== undefined) {
                        video.volume = volumeValue;
                    }
                    while (domid && domid.firstChild) {
                        domid.removeChild(domid.firstChild);
                    }
                    domid.appendChild(video);
                    MrtcSession.attachMediaStream(video, stream);
                    if (isSafari) {
                        video.play();
                    }
                },
                oncleanup: function () {
                    var domid = playerMap.get(streamKey);
                    while (domid && domid.firstChild) {
                        domid.removeChild(domid.firstChild);
                    }
                    streamPar.feed = undefined;
                }
            });
        }

        this.watchMedia = function (streamParam, videoTag) {
            if (!videoTag) return;
            var streamPar = streamMap.get(streamParam.streamKey);
            playerMap.forEach(function (value, key, map) {
                if (videoTag === value) {
                    playerMap.delete(key);
                }
            });
            playerMap.set(streamParam.streamKey, videoTag);
            if (streamPar) {
                // 如果视频列表内有目标视频流，直接拉流
                if (streamPar.feed) {
                    streamPar.feed.hangup();
                }
                newRemoteFeed(streamParam.streamKey);
                return;
            }
            // 加入等待播放刘表
            playerList.push(streamParam);
            // 等初始化完成再调用加入房间。否则直接加入房间。
            if (!initsuccess) return;
            attachRoom(streamParam, false);
        };

        this.stopWatchMedia = function (streamParam) {
            var streamPar = streamMap.get(streamParam.streamKey);
            if (streamPar === undefined || streamPar.feed === undefined) return;
            streamPar.feed.hangup();
        }

        this.setPlayVolume = function (streamParam, value) {
            var videoTag = playerMap.get(streamParam.streamKey);
            playerVolumeMap.set(streamParam.streamKey, value);
            if (videoTag.firstChild) {
                videoTag.firstChild.volume = value;
            }
        }

        this.pushMedia = function (streamParam, videoTag) {
            if (!videoTag) return;
            var streamPar = streamMap.get(streamParam.streamKey);
            playerMap.forEach(function (value, key, map) {
                if (videoTag === value) {
                    playerMap.delete(key);
                }
            });
            publishMap.set(streamParam.streamKey, videoTag);
            publishList.push(streamParam);
            if (!initsuccess) return;
            // 句柄存在，说明视频已经连接过，直接重新连接；否则从新加入房间
            var handle = publishHandleMap.get(streamParam.streamKey);
            if (handle) {
                publishOwnFeed(streamParam, true);
            } else {
                attachRoom(streamParam, true);
            }
        }

        this.stopPushMedia = function (streamParam) {
            // 得到操作句柄发送停止推流
            var handle = publishHandleMap.get(streamParam.streamKey);
            if (handle === undefined) return;
            var unpublish = {
                "request": "unpublish"
            };
            handle.send({
                "message": unpublish
            });
        };

        this.togglePublishMute = function (streamParam) {
            var handle = publishHandleMap.get(streamParam.streamKey);
            if (handle === undefined) return;
            var muted = handle.isAudioMuted();
            if (muted) {
                handle.unmuteAudio();
            } else {
                handle.muteAudio();
            }
        }
    }

    function post(url, data, token, fn) {
        var oAjax = new XMLHttpRequest();
        if (token) {
            oAjax.setRequestHeader('Authorization', 'Token ' + token)
        }
        oAjax.open('post', url, true);
        oAjax.setRequestHeader("Content-type", "application/json");
        oAjax.send(data);
        oAjax.onreadystatechange = function () {
            if (oAjax.readyState == 4) {
                if (fn) {
                    fn(oAjax.responseText)
                }
            }
        };
    }

    function Watchdog(domid, config) {
        // 一个watchdog对象保存一个url参数，和dom
        var vedioTarget = domid,
            urlProperty = undefined,
            random = Math.floor(Math.random() * 10000 + 1),
            streamParam = undefined,
            audioDeviceId = (config && config.audioDeviceId) ? config.audioDeviceId : undefined,
            videoDeviceId = (config && config.videoDeviceId) ? config.videoDeviceId : undefined,
            openCamera = (config && config.openCamera) ? config.openCamera : true,
            codec = (config && config.codec) ? config.codec : 'h264',
            published = false,
            state = 'previewCamera',
            callback = {};

        permission()

        function dispatch(name, value) {
            if (callback && callback[name] && callback[name].length) {
                callback[name].forEach((fn) => {
                    fn(value)
                })
            }
        }

        function permission() {

            if (navigator && navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
                navigator.mediaDevices.getUserMedia({video: true, audio: true}).then((res) => {
                    dispatch('access', true)
                }).catch((err) => {
                    // console.log(err)
                    dispatch('access', false)
                })
            } else {
                dispatch('access', false)
            }
        }

        this.play = function (url) {
            if (url === undefined || url === "") return;
            this.stop();
            urlProperty = parseURL(url);
            if (urlProperty.protocol !== "mrtc") {
                console.log("protocol error", urlProperty.protocol);
                return;
            }
            streamParam = new StreamParam(urlProperty.room, urlProperty.name, random);
            if (streamParam == undefined) return;
            // 如果没有对应host的client，新创建一个。
            var client = MrtcSessionMap.get(urlProperty.host);
            if (client === undefined) {
                client = new MrtcSessionClient(urlProperty.host);
                MrtcSessionMap.set(urlProperty.host, client);
            }
            if (!streamParam.params) {
                streamParam.params = {codec: codec}
            } else {
                streamParam.params.codec = codec
            }
            client.watchMedia(streamParam, vedioTarget)
        };

        this.stop = function () {
            if (urlProperty === undefined) return;
            var client = MrtcSessionMap.get(urlProperty.host);
            if (client === undefined || streamParam === undefined) return;
            client.stopWatchMedia(streamParam);
        };

        function publish(url, params) {
            if (!url) return;
            urlProperty = parseURL(url);
            if (urlProperty.protocol !== "mrtc") {
                console.log("protocol error", urlProperty.protocol);
                return;
            }
            streamParam = new StreamParam(urlProperty.room, urlProperty.name, random, params);
            if (!streamParam) return;
            var client = MrtcSessionMap.get(urlProperty.host);
            if (!client) {
                client = new MrtcSessionClient(urlProperty.host);
                MrtcSessionMap.set(urlProperty.host, client);
            }
            published = true
            client.pushMedia(streamParam, vedioTarget);
        }

        this.subscribe = function (eventName, fn) {
            if (callback[eventName] !== undefined) {
                callback[eventName].push(fn)
            } else {
                callback[eventName] = [fn]
            }
        }

        this.unSubscribe = function (eventName, fn) {
            if (callback[eventName] !== undefined) {
                callback[eventName] = callback[eventName].filter((val) => {
                    val != fn;
                })
            }
        }

        this.publishCamera = function (url, config = {}) {
            if (published) {

                this.unpublish(function (param) {
                    if(!unpublishCallback[param.streamKey]){
                        unpublishCallback[param.streamKey] = {}
                    }
                    unpublishCallback[param.streamKey].unpublishCallback = function () {
                        state = 'publishCamera'
                        publish(url, Object.assign({
                            type: "camera",
                            audioDeviceId: audioDeviceId,
                            videoDeviceId: videoDeviceId,
                            openCamera: openCamera,
                            codec: codec,
                            callback: callback
                        }, config));
                    }
                });
            } else {
                state = 'publishCamera'
                publish(url, Object.assign({
                    type: "camera",
                    audioDeviceId: audioDeviceId,
                    videoDeviceId: videoDeviceId,
                    openCamera: openCamera,
                    codec: codec,
                    callback: callback
                }, config));
            }
        };

        this.publishScreen = function (url, config = {}) {
            if (published) {

                this.unpublish(function (param) {
                    if(!unpublishCallback[param.streamKey]){
                        unpublishCallback[param.streamKey] = {}
                    }
                    unpublishCallback[param.streamKey].unpublishCallback = function () {
                        publish(url, Object.assign({type: "screen", codec: codec, callback: callback}, config));
                    }
                });
            } else {
                publish(url, Object.assign({type: "screen", codec: codec, callback: callback}, config));
            }
        };

        this.publishScreenAndCamera = function (url, params) {
            if (published) {

                this.unpublish(function (param) {
                    if(!unpublishCallback[param.streamKey]){
                        unpublishCallback[param.streamKey] = {}
                    }
                    unpublishCallback[param.streamKey].unpublishCallback = function () {
                        var location = 2;
                        var scale = 0.3;
                        if (params) {
                            scale = params.scale;
                            location = params.location;
                        }
                        publish(url, {type: "screenandcamera", scale: scale, location: location, codec: codec})
                    }
                });
            } else {
                var location = 2;
                var scale = 0.3;
                if (params) {
                    scale = params.scale;
                    location = params.location;
                }
                publish(url, {type: "screenandcamera", scale: scale, location: location})
            }

        };


        this.switchCamera = function (url, videoId) {
            this.setVideoDeviceId(videoId)
            this.reStart(url)

        }

        this.switchAudio = function (url, audioId) {
            this.setAudioDeviceId(audioId)
            this.reStart(url)
        }

        this.reStart = function (url) {
            if (state === 'previewCamera') {
                this.previewCamera()
            } else if (state === 'publishCamera') {
                this.publishCamera(url);
            }
        }

        this.unpublish = function (callback) {
            published = false
            if (!urlProperty) {
                callback(streamParam);
                return;
            }

            var client = MrtcSessionMap.get(urlProperty.host);
            if (client === undefined || streamParam === undefined) {
                callback(streamParam);
                return;
            }
            if (callback){
                callback(streamParam);
            }
            client.stopPushMedia(streamParam);
        };

        this.setPlayVolume = function (value) {
            var client = MrtcSessionMap.get(urlProperty.host);
            if (!client || !streamParam) return;
            client.setPlayVolume(streamParam, value);
        };

        this.togglePublishMute = function () {
            var client = MrtcSessionMap.get(urlProperty.host);
            if (!client || !streamParam) return;
            client.togglePublishMute(streamParam);
        };

        this.setVideoDeviceId = function (id) {
            videoDeviceId = id
        }

        this.setAudioDeviceId = function (id) {
            audioDeviceId = id
        }

        this.setCamera = function (open) {
            openCamera = open
        }

        this.previewCamera = function () {
            state = 'previewCamera'

            var constraints = {
                audio: {
                    deviceId: {
                        exact: audioDeviceId
                    }
                },
                video: {
                    deviceId: {
                        exact: videoDeviceId
                    },
                    frameRate: {
                        min: 5,
                        max: 25,
                    }
                }
            };
            if (!openCamera) {
                constraints.video = false;
            }

            navigator.mediaDevices.getUserMedia(constraints)
                .then(function (stream) {
                    localStream = stream;
                    var domid = vedioTarget;
                    while (domid.firstChild) {
                        domid.removeChild(domid.firstChild);
                    }
                    var video = document.createElement('video');
                    video.setAttribute('id', 'localVideo')
                    video.setAttribute('width', '100%')
                    video.setAttribute('height', '100%')
                    video.setAttribute('autoplay', '')
                    video.setAttribute('playsinline', '')
                    video.setAttribute('controls', '')
                    video.muted = true;
                    // MrtcSession.attachMediaStream(video, stream);
                    video.srcObject = stream;
                    domid.appendChild(video);
                    if (isSafari) {
                        video.play();
                    }
                })
                .catch(function (error) {
                    console.log("WebRTC error:", error);
                });
        }
    }

    function parseURL(url) {
        var pattern = RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?");
        var matches = url.match(pattern);
        return {
            protocol: matches[2], // mrtc
            host: matches[4],
            room: matches[5].replace(/^\//, '').split('/')[0],
            name: matches[5].replace(/^\//, '').split('/')[1],
            params: matches[7] !== undefined ? (function () {
                var ret = {},
                    seg = matches[7].replace(/^\?/, '').split('&'),
                    len = seg.length,
                    i = 0,
                    s;
                for (; i < len; i++) {
                    if (!seg[i]) {
                        continue;
                    }
                    s = seg[i].split('=');
                    ret[s[0]] = s[1];
                }
                return ret;
            })() : [],
            fragment: matches[9] // #
        };
    }

    return {
        setup: function (domid, config) {
            // 一个watchdog对应路流
            var watchdog = new Watchdog(domid, config);
            return watchdog;
        },
        isWebrtcSupported :function () {
            window.RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection;
            return window.RTCPeerConnection !== undefined && window.RTCPeerConnection !== null;
        },
        detectExtensions:function (success,error) {
            getScreenConstraints((msg)=>{
                if(msg == "installed-disabled"){
                    error()
                }else{
                    success()
                }
            })
        }
    };
})();